//
//  OSS.swift
//  RaveLand
//
//  Created by zhuxuhong on 2021/4/25.
//  Copyright © 2021 CSLY. All rights reserved.
//

import Foundation
import AliyunOSSiOS

public class OSS {
    public let idKey: String
    public let secretKey: String
    public let bucketName: String
    public let endpointUrl: String
    
    public init(idKey: String, secretKey: String, bucketName: String, endpointUrl: String){
        self.idKey = idKey
        self.secretKey = secretKey
        self.bucketName = bucketName
        self.endpointUrl = endpointUrl
    }
    
    open lazy var client: OSSClient = {
        let v = OSSCustomSignerCredentialProvider { (sign, err) -> String? in
            guard let v = OSSUtil.calBase64Sha1(withData: sign, withSecret: self.secretKey) else {
                fatalError("OSS 加密失败!")
            }
            return "OSS \(self.idKey):\(v)"
        }
        guard let provider = v else {
            fatalError("OSS Client 初始化失败!")
        }
        return OSSClient(endpoint: endpointUrl, credentialProvider: provider)
    }()
}

extension OSS {
    public enum FileType: String {
        case jpg, jpeg, png
        case mov, mp4
        case mp3
        case zip
    }
}

extension OSS {
    /// 上传单个视频
    public func uploadVideo(
        _ url: String,
        objectKey: String,
        progress: BlockT<Float>?,
        success: BlockT<String>?,
        failure: BlockT<Error>?
    ) {
        GCD.asyncInGlobalQueue
        {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: url))
                self.uploadFile(data, objectKey: objectKey) { v in
                    GCD.asyncInMainQueue { progress?(v) }
                }
                success: { v in
                    GCD.asyncInMainQueue{ success?(v) }
                }
                failure: { v in
                    GCD.asyncInMainQueue { failure?(v) }
                }
            } catch {
                GCD.asyncInMainQueue { failure?(error) }
            }
        }
    }
    
    /// 上传文件
    public func uploadFile(
        _ data: Data,
        objectKey: String,
        progress: BlockT<Float>?,
        success: BlockT<String>?,
        failure: BlockT<Error>?
    ) {
        let request = OSSPutObjectRequest()
        request.bucketName = bucketName
        request.objectKey = objectKey
        request.uploadingData = data
        request.uploadProgress = { _,sended,expected in
            GCD.asyncInMainQueue {
                progress?(Float(sended) / Float(expected))
            }
        }
        
        let task = client.putObject(request)
        task.continue({
            debugPrint($0)
            if let err = $0.error {
                GCD.asyncInMainQueue{ failure?(err) }
                print("======= [OSS File Upload Error] [\(err.localizedDescription)]")
            }
            else {
                GCD.asyncInMainQueue{ success?(request.objectKey) }
            }
            return nil
        })
        task.waitUntilFinished()
    }
    
    /// 上传图片
    public func uploadImages(
        _ imgs: [Data],
        objectKeyHandler: @escaping BlockTR<Data, String>,
        progress: BlockTT<Int, Float>?,
        success: BlockT<[String]>?,
        failure: BlockT<Error>?
    ) {
        let queue = OperationQueue()
        var urls: [String] = []
        for (i, data) in imgs.enumerated()
        {
            let op = BlockOperation {
                self.uploadFile(data, objectKey: objectKeyHandler(data)) { p in
                    GCD.asyncInMainQueue { progress?(i+1, p) }
                }
                success: {
                    urls.append($0)
                    if i == imgs.count-1 {
                        GCD.asyncInMainQueue{ success?(urls) }
                    }
                }
                failure: { err in
                    GCD.asyncInMainQueue{ failure?(err) }
                }
            }
            queue.addOperation(op)
            if queue.operations.count > 1,
                let lastOp = queue.operations.last,
                op != lastOp {
                op.addDependency(lastOp)
            }
        }
    }
}
